#include "gamekit/components/ScriptingComponent.h"

#include <iostream>
#include <string>

extern "C"
{
	#include <gamekit-lua/lua.h>
	#include <gamekit-lua/lualib.h>
	#include <gamekit-lua/lauxlib.h>
}

#include "gamekit/scripting/Wrappers.h"
#include "gamekit/scripting/Result.h"
#include "gamekit/scripting/Arguments.h"
#include "gamekit/scripting/Invocation.h"
#include "gamekit/common/Delegate.h"

using namespace gamekit;
using namespace gamekit::scripting;

ScriptingComponent::ScriptingComponent(Scene* scene)
	: Component(scene),
	m_state(NULL),
	m_initialized(false)
{
	createModule("__GLOBAL__");
}

ScriptingComponent::~ScriptingComponent()
{
	ModuleMapIter it;

	for(it = m_modules.begin(); it != m_modules.end(); it++)
	{
		delete it->second;
	}

	m_modules.clear();
	closeLua();
}

void ScriptingComponent::onInit(void)
{
	openLua();

	ModuleMapIter it;

	for(it = m_modules.begin(); it != m_modules.end(); it++)
	{
		if(it->first == "__GLOBAL__")
		{
			registerGlobalModule(it->second);
		}
		else
		{
			registerModule(it->second);
		}
	}

	m_initialized = true;
}

void ScriptingComponent::execFile(const std::string& file)
{
	if(m_initialized)
	{
		return;
	}

	luaL_dofile(m_state, file.c_str());
}

void ScriptingComponent::exec(const std::string& script)
{
	if(!m_initialized)
	{
		return;
	}

	int r = 0;

	r = luaL_loadstring(m_state, script.c_str());
	if( r != 0 )
	{
		printError();
		return;
	}

	r = lua_pcall(m_state, 0, LUA_MULTRET, 0);
	if( r != 0 )
	{
		printError();
		return;
	}
}

void ScriptingComponent::printError(void)
{
	std::cerr << "-- " << lua_tostring(m_state, -1) << std::endl;
	lua_pop(m_state, 1);
}

void ScriptingComponent::closeLua(void)
{
	if(m_state != NULL)
	{
		lua_close(m_state);
	}

	m_state = NULL;
}

void ScriptingComponent::openLua(void)
{
	if(m_state != NULL)
	{
		closeLua();
	}

	m_state = lua_open();
	luaopen_base(m_state);
}

ModuleBuilder ScriptingComponent::function(const std::string& name, LuaFunction func)
{
	ModuleBuilder builder(getModule("__GLOBAL__"));
	builder.function(name, func);
	return builder;
}

ModuleBuilder ScriptingComponent::module(const std::string name)
{
	ModuleBuilder builder(getModule(name));
	return builder;
}

Module* ScriptingComponent::getModule(const std::string& name)
{
	ModuleMapIter it = m_modules.find(name);
	if(it == m_modules.end())
	{
		return createModule(name);
	}
	return it->second;
}

Module* ScriptingComponent::createModule(const std::string& name)
{
	Module* module = new Module(name);
	m_modules.insert(std::pair<std::string, Module*>(name, module));
	return module;
}

void ScriptingComponent::registerGlobalModule(Module* module)
{
	Module::FunctionMap functions = module->m_functions;
	Module::FunctionMapIter it;

	for(it = functions.begin(); it != functions.end(); it++)
	{
		const char* name = it->first.c_str();
		lua_register(m_state, name, it->second);
	}
}

void ScriptingComponent::registerModule(Module* module)
{
	Module::FunctionMap functions = module->m_functions;
	Module::FunctionMapIter it;

	luaL_Reg* luaModule = new luaL_Reg[functions.size() + 1];

	int i;
	for(it = functions.begin(), i = 0; it != functions.end(); it++, i++)
	{
		luaL_Reg r;
		r.name = it->first.c_str();
		r.func = it->second;
		luaModule[i] = r;
	}

	luaL_Reg nil;
	nil.name = 0;
	nil.func = 0;
	luaModule[functions.size()] = nil;

	luaL_register(m_state, module->m_name.c_str(), luaModule);
}
